1   package org.apache.lucene.util;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one or more
5    * contributor license agreements.  See the NOTICE file distributed with
6    * this work for additional information regarding copyright ownership.
7    * The ASF licenses this file to You under the Apache License, Version 2.0
8    * (the "License"); you may not use this file except in compliance with
9    * the License.  You may obtain a copy of the License at
10   *
11   *     http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the License for the specific language governing permissions and
17   * limitations under the License.
18   */
19  
20  import java.util.Arrays;
21  import java.util.Collection;
22  import java.util.Comparator;
23  
24  /**
25   * Methods for manipulating arrays.
26   *
27   * @lucene.internal
28   */
29  
30  public final class ArrayUtil {
31  
32    /** Maximum length for an array (Integer.MAX_VALUE - RamUsageEstimator.NUM_BYTES_ARRAY_HEADER). */
33    public static final int MAX_ARRAY_LENGTH = Integer.MAX_VALUE - RamUsageEstimator.NUM_BYTES_ARRAY_HEADER;
34  
35    private ArrayUtil() {} // no instance
36  
37    /*
38       Begin Apache Harmony code
39  
40       Revision taken on Friday, June 12. https://svn.apache.org/repos/asf/harmony/enhanced/classlib/archive/java6/modules/luni/src/main/java/java/lang/Integer.java
41  
42     */
43  
44    /**
45     * Parses the string argument as if it was an int value and returns the
46     * result. Throws NumberFormatException if the string does not represent an
47     * int quantity.
48     *
49     * @param chars a string representation of an int quantity.
50     * @return int the value represented by the argument
51     * @throws NumberFormatException if the argument could not be parsed as an int quantity.
52     */
53    public static int parseInt(char[] chars) throws NumberFormatException {
54      return parseInt(chars, 0, chars.length, 10);
55    }
56  
57    /**
58     * Parses a char array into an int.
59     * @param chars the character array
60     * @param offset The offset into the array
61     * @param len The length
62     * @return the int
63     * @throws NumberFormatException if it can't parse
64     */
65    public static int parseInt(char[] chars, int offset, int len) throws NumberFormatException {
66      return parseInt(chars, offset, len, 10);
67    }
68  
69    /**
70     * Parses the string argument as if it was an int value and returns the
71     * result. Throws NumberFormatException if the string does not represent an
72     * int quantity. The second argument specifies the radix to use when parsing
73     * the value.
74     *
75     * @param chars a string representation of an int quantity.
76     * @param radix the base to use for conversion.
77     * @return int the value represented by the argument
78     * @throws NumberFormatException if the argument could not be parsed as an int quantity.
79     */
80    public static int parseInt(char[] chars, int offset, int len, int radix)
81            throws NumberFormatException {
82      if (chars == null || radix < Character.MIN_RADIX
83              || radix > Character.MAX_RADIX) {
84        throw new NumberFormatException();
85      }
86      int  i = 0;
87      if (len == 0) {
88        throw new NumberFormatException("chars length is 0");
89      }
90      boolean negative = chars[offset + i] == '-';
91      if (negative && ++i == len) {
92        throw new NumberFormatException("can't convert to an int");
93      }
94      if (negative == true){
95        offset++;
96        len--;
97      }
98      return parse(chars, offset, len, radix, negative);
99    }
100 
101 
102   private static int parse(char[] chars, int offset, int len, int radix,
103                            boolean negative) throws NumberFormatException {
104     int max = Integer.MIN_VALUE / radix;
105     int result = 0;
106     for (int i = 0; i < len; i++){
107       int digit = Character.digit(chars[i + offset], radix);
108       if (digit == -1) {
109         throw new NumberFormatException("Unable to parse");
110       }
111       if (max > result) {
112         throw new NumberFormatException("Unable to parse");
113       }
114       int next = result * radix - digit;
115       if (next > result) {
116         throw new NumberFormatException("Unable to parse");
117       }
118       result = next;
119     }
120     /*while (offset < len) {
121 
122     }*/
123     if (!negative) {
124       result = -result;
125       if (result < 0) {
126         throw new NumberFormatException("Unable to parse");
127       }
128     }
129     return result;
130   }
131 
132 
133   /*
134 
135  END APACHE HARMONY CODE
136   */
137 
138   /** Returns an array size &gt;= minTargetSize, generally
139    *  over-allocating exponentially to achieve amortized
140    *  linear-time cost as the array grows.
141    *
142    *  NOTE: this was originally borrowed from Python 2.4.2
143    *  listobject.c sources (attribution in LICENSE.txt), but
144    *  has now been substantially changed based on
145    *  discussions from java-dev thread with subject "Dynamic
146    *  array reallocation algorithms", started on Jan 12
147    *  2010.
148    *
149    * @param minTargetSize Minimum required value to be returned.
150    * @param bytesPerElement Bytes used by each element of
151    * the array.  See constants in {@link RamUsageEstimator}.
152    *
153    * @lucene.internal
154    */
155 
156   public static int oversize(int minTargetSize, int bytesPerElement) {
157 
158     if (minTargetSize < 0) {
159       // catch usage that accidentally overflows int
160       throw new IllegalArgumentException("invalid array size " + minTargetSize);
161     }
162 
163     if (minTargetSize == 0) {
164       // wait until at least one element is requested
165       return 0;
166     }
167 
168     if (minTargetSize > MAX_ARRAY_LENGTH) {
169       throw new IllegalArgumentException("requested array size " + minTargetSize + " exceeds maximum array in java (" + MAX_ARRAY_LENGTH + ")");
170     }
171 
172     // asymptotic exponential growth by 1/8th, favors
173     // spending a bit more CPU to not tie up too much wasted
174     // RAM:
175     int extra = minTargetSize >> 3;
176 
177     if (extra < 3) {
178       // for very small arrays, where constant overhead of
179       // realloc is presumably relatively high, we grow
180       // faster
181       extra = 3;
182     }
183 
184     int newSize = minTargetSize + extra;
185 
186     // add 7 to allow for worst case byte alignment addition below:
187     if (newSize+7 < 0 || newSize+7 > MAX_ARRAY_LENGTH) {
188       // int overflowed, or we exceeded the maximum array length
189       return MAX_ARRAY_LENGTH;
190     }
191 
192     if (Constants.JRE_IS_64BIT) {
193       // round up to 8 byte alignment in 64bit env
194       switch(bytesPerElement) {
195       case 4:
196         // round up to multiple of 2
197         return (newSize + 1) & 0x7ffffffe;
198       case 2:
199         // round up to multiple of 4
200         return (newSize + 3) & 0x7ffffffc;
201       case 1:
202         // round up to multiple of 8
203         return (newSize + 7) & 0x7ffffff8;
204       case 8:
205         // no rounding
206       default:
207         // odd (invalid?) size
208         return newSize;
209       }
210     } else {
211       // round up to 4 byte alignment in 64bit env
212       switch(bytesPerElement) {
213       case 2:
214         // round up to multiple of 2
215         return (newSize + 1) & 0x7ffffffe;
216       case 1:
217         // round up to multiple of 4
218         return (newSize + 3) & 0x7ffffffc;
219       case 4:
220       case 8:
221         // no rounding
222       default:
223         // odd (invalid?) size
224         return newSize;
225       }
226     }
227   }
228 
229   public static int getShrinkSize(int currentSize, int targetSize, int bytesPerElement) {
230     final int newSize = oversize(targetSize, bytesPerElement);
231     // Only reallocate if we are "substantially" smaller.
232     // This saves us from "running hot" (constantly making a
233     // bit bigger then a bit smaller, over and over):
234     if (newSize < currentSize / 2)
235       return newSize;
236     else
237       return currentSize;
238   }
239 
240   public static <T> T[] grow(T[] array, int minSize) {
241     assert minSize >= 0: "size must be positive (got " + minSize + "): likely integer overflow?";
242     if (array.length < minSize) {
243       return Arrays.copyOf(array, oversize(minSize, RamUsageEstimator.NUM_BYTES_OBJECT_REF));
244     } else
245       return array;
246   }
247 
248   public static short[] grow(short[] array, int minSize) {
249     assert minSize >= 0: "size must be positive (got " + minSize + "): likely integer overflow?";
250     if (array.length < minSize) {
251       short[] newArray = new short[oversize(minSize, RamUsageEstimator.NUM_BYTES_SHORT)];
252       System.arraycopy(array, 0, newArray, 0, array.length);
253       return newArray;
254     } else
255       return array;
256   }
257 
258   public static short[] grow(short[] array) {
259     return grow(array, 1 + array.length);
260   }
261   
262   public static float[] grow(float[] array, int minSize) {
263     assert minSize >= 0: "size must be positive (got " + minSize + "): likely integer overflow?";
264     if (array.length < minSize) {
265       float[] newArray = new float[oversize(minSize, RamUsageEstimator.NUM_BYTES_FLOAT)];
266       System.arraycopy(array, 0, newArray, 0, array.length);
267       return newArray;
268     } else
269       return array;
270   }
271 
272   public static float[] grow(float[] array) {
273     return grow(array, 1 + array.length);
274   }
275 
276   public static double[] grow(double[] array, int minSize) {
277     assert minSize >= 0: "size must be positive (got " + minSize + "): likely integer overflow?";
278     if (array.length < minSize) {
279       double[] newArray = new double[oversize(minSize, RamUsageEstimator.NUM_BYTES_DOUBLE)];
280       System.arraycopy(array, 0, newArray, 0, array.length);
281       return newArray;
282     } else
283       return array;
284   }
285 
286   public static double[] grow(double[] array) {
287     return grow(array, 1 + array.length);
288   }
289 
290   public static short[] shrink(short[] array, int targetSize) {
291     assert targetSize >= 0: "size must be positive (got " + targetSize + "): likely integer overflow?";
292     final int newSize = getShrinkSize(array.length, targetSize, RamUsageEstimator.NUM_BYTES_SHORT);
293     if (newSize != array.length) {
294       short[] newArray = new short[newSize];
295       System.arraycopy(array, 0, newArray, 0, newSize);
296       return newArray;
297     } else
298       return array;
299   }
300 
301   public static int[] grow(int[] array, int minSize) {
302     assert minSize >= 0: "size must be positive (got " + minSize + "): likely integer overflow?";
303     if (array.length < minSize) {
304       int[] newArray = new int[oversize(minSize, RamUsageEstimator.NUM_BYTES_INT)];
305       System.arraycopy(array, 0, newArray, 0, array.length);
306       return newArray;
307     } else
308       return array;
309   }
310 
311   public static int[] grow(int[] array) {
312     return grow(array, 1 + array.length);
313   }
314 
315   public static int[] shrink(int[] array, int targetSize) {
316     assert targetSize >= 0: "size must be positive (got " + targetSize + "): likely integer overflow?";
317     final int newSize = getShrinkSize(array.length, targetSize, RamUsageEstimator.NUM_BYTES_INT);
318     if (newSize != array.length) {
319       int[] newArray = new int[newSize];
320       System.arraycopy(array, 0, newArray, 0, newSize);
321       return newArray;
322     } else
323       return array;
324   }
325 
326   public static long[] grow(long[] array, int minSize) {
327     assert minSize >= 0: "size must be positive (got " + minSize + "): likely integer overflow?";
328     if (array.length < minSize) {
329       long[] newArray = new long[oversize(minSize, RamUsageEstimator.NUM_BYTES_LONG)];
330       System.arraycopy(array, 0, newArray, 0, array.length);
331       return newArray;
332     } else
333       return array;
334   }
335 
336   public static long[] grow(long[] array) {
337     return grow(array, 1 + array.length);
338   }
339 
340   public static long[] shrink(long[] array, int targetSize) {
341     assert targetSize >= 0: "size must be positive (got " + targetSize + "): likely integer overflow?";
342     final int newSize = getShrinkSize(array.length, targetSize, RamUsageEstimator.NUM_BYTES_LONG);
343     if (newSize != array.length) {
344       long[] newArray = new long[newSize];
345       System.arraycopy(array, 0, newArray, 0, newSize);
346       return newArray;
347     } else
348       return array;
349   }
350 
351   public static byte[] grow(byte[] array, int minSize) {
352     assert minSize >= 0: "size must be positive (got " + minSize + "): likely integer overflow?";
353     if (array.length < minSize) {
354       byte[] newArray = new byte[oversize(minSize, 1)];
355       System.arraycopy(array, 0, newArray, 0, array.length);
356       return newArray;
357     } else
358       return array;
359   }
360 
361   public static byte[] grow(byte[] array) {
362     return grow(array, 1 + array.length);
363   }
364 
365   public static byte[] shrink(byte[] array, int targetSize) {
366     assert targetSize >= 0: "size must be positive (got " + targetSize + "): likely integer overflow?";
367     final int newSize = getShrinkSize(array.length, targetSize, 1);
368     if (newSize != array.length) {
369       byte[] newArray = new byte[newSize];
370       System.arraycopy(array, 0, newArray, 0, newSize);
371       return newArray;
372     } else
373       return array;
374   }
375 
376   public static boolean[] grow(boolean[] array, int minSize) {
377     assert minSize >= 0: "size must be positive (got " + minSize + "): likely integer overflow?";
378     if (array.length < minSize) {
379       boolean[] newArray = new boolean[oversize(minSize, 1)];
380       System.arraycopy(array, 0, newArray, 0, array.length);
381       return newArray;
382     } else
383       return array;
384   }
385 
386   public static boolean[] grow(boolean[] array) {
387     return grow(array, 1 + array.length);
388   }
389 
390   public static boolean[] shrink(boolean[] array, int targetSize) {
391     assert targetSize >= 0: "size must be positive (got " + targetSize + "): likely integer overflow?";
392     final int newSize = getShrinkSize(array.length, targetSize, 1);
393     if (newSize != array.length) {
394       boolean[] newArray = new boolean[newSize];
395       System.arraycopy(array, 0, newArray, 0, newSize);
396       return newArray;
397     } else
398       return array;
399   }
400 
401   public static char[] grow(char[] array, int minSize) {
402     assert minSize >= 0: "size must be positive (got " + minSize + "): likely integer overflow?";
403     if (array.length < minSize) {
404       char[] newArray = new char[oversize(minSize, RamUsageEstimator.NUM_BYTES_CHAR)];
405       System.arraycopy(array, 0, newArray, 0, array.length);
406       return newArray;
407     } else
408       return array;
409   }
410 
411   public static char[] grow(char[] array) {
412     return grow(array, 1 + array.length);
413   }
414 
415   public static char[] shrink(char[] array, int targetSize) {
416     assert targetSize >= 0: "size must be positive (got " + targetSize + "): likely integer overflow?";
417     final int newSize = getShrinkSize(array.length, targetSize, RamUsageEstimator.NUM_BYTES_CHAR);
418     if (newSize != array.length) {
419       char[] newArray = new char[newSize];
420       System.arraycopy(array, 0, newArray, 0, newSize);
421       return newArray;
422     } else
423       return array;
424   }
425 
426   public static int[][] grow(int[][] array, int minSize) {
427     assert minSize >= 0: "size must be positive (got " + minSize + "): likely integer overflow?";
428     if (array.length < minSize) {
429       int[][] newArray = new int[oversize(minSize, RamUsageEstimator.NUM_BYTES_OBJECT_REF)][];
430       System.arraycopy(array, 0, newArray, 0, array.length);
431       return newArray;
432     } else {
433       return array;
434     }
435   }
436 
437   public static int[][] grow(int[][] array) {
438     return grow(array, 1 + array.length);
439   }
440 
441   public static int[][] shrink(int[][] array, int targetSize) {
442     assert targetSize >= 0: "size must be positive (got " + targetSize + "): likely integer overflow?";
443     final int newSize = getShrinkSize(array.length, targetSize, RamUsageEstimator.NUM_BYTES_OBJECT_REF);
444     if (newSize != array.length) {
445       int[][] newArray = new int[newSize][];
446       System.arraycopy(array, 0, newArray, 0, newSize);
447       return newArray;
448     } else {
449       return array;
450     }
451   }
452 
453   public static float[][] grow(float[][] array, int minSize) {
454     assert minSize >= 0: "size must be positive (got " + minSize + "): likely integer overflow?";
455     if (array.length < minSize) {
456       float[][] newArray = new float[oversize(minSize, RamUsageEstimator.NUM_BYTES_OBJECT_REF)][];
457       System.arraycopy(array, 0, newArray, 0, array.length);
458       return newArray;
459     } else {
460       return array;
461     }
462   }
463 
464   public static float[][] grow(float[][] array) {
465     return grow(array, 1 + array.length);
466   }
467 
468   public static float[][] shrink(float[][] array, int targetSize) {
469     assert targetSize >= 0: "size must be positive (got " + targetSize + "): likely integer overflow?";
470     final int newSize = getShrinkSize(array.length, targetSize, RamUsageEstimator.NUM_BYTES_OBJECT_REF);
471     if (newSize != array.length) {
472       float[][] newArray = new float[newSize][];
473       System.arraycopy(array, 0, newArray, 0, newSize);
474       return newArray;
475     } else {
476       return array;
477     }
478   }
479 
480   /**
481    * Returns hash of chars in range start (inclusive) to
482    * end (inclusive)
483    */
484   public static int hashCode(char[] array, int start, int end) {
485     int code = 0;
486     for (int i = end - 1; i >= start; i--)
487       code = code * 31 + array[i];
488     return code;
489   }
490 
491   /**
492    * Returns hash of bytes in range start (inclusive) to
493    * end (inclusive)
494    */
495   public static int hashCode(byte[] array, int start, int end) {
496     int code = 0;
497     for (int i = end - 1; i >= start; i--)
498       code = code * 31 + array[i];
499     return code;
500   }
501 
502 
503   // Since Arrays.equals doesn't implement offsets for equals
504   /**
505    * See if two array slices are the same.
506    *
507    * @param left        The left array to compare
508    * @param offsetLeft  The offset into the array.  Must be positive
509    * @param right       The right array to compare
510    * @param offsetRight the offset into the right array.  Must be positive
511    * @param length      The length of the section of the array to compare
512    * @return true if the two arrays, starting at their respective offsets, are equal
513    * 
514    * @see java.util.Arrays#equals(char[], char[])
515    */
516   public static boolean equals(char[] left, int offsetLeft, char[] right, int offsetRight, int length) {
517     if ((offsetLeft + length <= left.length) && (offsetRight + length <= right.length)) {
518       for (int i = 0; i < length; i++) {
519         if (left[offsetLeft + i] != right[offsetRight + i]) {
520           return false;
521         }
522 
523       }
524       return true;
525     }
526     return false;
527   }
528   
529   // Since Arrays.equals doesn't implement offsets for equals
530   /**
531    * See if two array slices are the same.
532    *
533    * @param left        The left array to compare
534    * @param offsetLeft  The offset into the array.  Must be positive
535    * @param right       The right array to compare
536    * @param offsetRight the offset into the right array.  Must be positive
537    * @param length      The length of the section of the array to compare
538    * @return true if the two arrays, starting at their respective offsets, are equal
539    * 
540    * @see java.util.Arrays#equals(byte[], byte[])
541    */
542   public static boolean equals(byte[] left, int offsetLeft, byte[] right, int offsetRight, int length) {
543     if ((offsetLeft + length <= left.length) && (offsetRight + length <= right.length)) {
544       for (int i = 0; i < length; i++) {
545         if (left[offsetLeft + i] != right[offsetRight + i]) {
546           return false;
547         }
548 
549       }
550       return true;
551     }
552     return false;
553   }
554 
555   /* DISABLE THIS FOR NOW: This has performance problems until Java creates intrinsics for Class#getComponentType() and Array.newInstance()
556   public static <T> T[] grow(T[] array, int minSize) {
557     assert minSize >= 0: "size must be positive (got " + minSize + "): likely integer overflow?";
558     if (array.length < minSize) {
559       @SuppressWarnings("unchecked") final T[] newArray =
560         (T[]) Array.newInstance(array.getClass().getComponentType(), oversize(minSize, RamUsageEstimator.NUM_BYTES_OBJECT_REF));
561       System.arraycopy(array, 0, newArray, 0, array.length);
562       return newArray;
563     } else
564       return array;
565   }
566 
567   public static <T> T[] grow(T[] array) {
568     return grow(array, 1 + array.length);
569   }
570 
571   public static <T> T[] shrink(T[] array, int targetSize) {
572     assert targetSize >= 0: "size must be positive (got " + targetSize + "): likely integer overflow?";
573     final int newSize = getShrinkSize(array.length, targetSize, RamUsageEstimator.NUM_BYTES_OBJECT_REF);
574     if (newSize != array.length) {
575       @SuppressWarnings("unchecked") final T[] newArray =
576         (T[]) Array.newInstance(array.getClass().getComponentType(), newSize);
577       System.arraycopy(array, 0, newArray, 0, newSize);
578       return newArray;
579     } else
580       return array;
581   }
582   */
583 
584   // Since Arrays.equals doesn't implement offsets for equals
585   /**
586    * See if two array slices are the same.
587    *
588    * @param left        The left array to compare
589    * @param offsetLeft  The offset into the array.  Must be positive
590    * @param right       The right array to compare
591    * @param offsetRight the offset into the right array.  Must be positive
592    * @param length      The length of the section of the array to compare
593    * @return true if the two arrays, starting at their respective offsets, are equal
594    * 
595    * @see java.util.Arrays#equals(char[], char[])
596    */
597   public static boolean equals(int[] left, int offsetLeft, int[] right, int offsetRight, int length) {
598     if ((offsetLeft + length <= left.length) && (offsetRight + length <= right.length)) {
599       for (int i = 0; i < length; i++) {
600         if (left[offsetLeft + i] != right[offsetRight + i]) {
601           return false;
602         }
603 
604       }
605       return true;
606     }
607     return false;
608   }
609 
610   public static int[] toIntArray(Collection<Integer> ints) {
611 
612     final int[] result = new int[ints.size()];
613     int upto = 0;
614     for(int v : ints) {
615       result[upto++] = v;
616     }
617 
618     // paranoia:
619     assert upto == result.length;
620 
621     return result;
622   }
623 
624   private static class NaturalComparator<T extends Comparable<? super T>> implements Comparator<T> {
625     NaturalComparator() {}
626     @Override
627     public int compare(T o1, T o2) {
628       return o1.compareTo(o2);
629     }
630   }
631 
632   private static final Comparator<?> NATURAL_COMPARATOR = new NaturalComparator<>();
633 
634   /** Get the natural {@link Comparator} for the provided object class. */
635   @SuppressWarnings("unchecked")
636   public static <T extends Comparable<? super T>> Comparator<T> naturalComparator() {
637     return (Comparator<T>) NATURAL_COMPARATOR;
638   }
639 
640   /** Swap values stored in slots <code>i</code> and <code>j</code> */
641   public static <T> void swap(T[] arr, int i, int j) {
642     final T tmp = arr[i];
643     arr[i] = arr[j];
644     arr[j] = tmp;
645   }
646 
647   // intro-sorts
648   
649   /**
650    * Sorts the given array slice using the {@link Comparator}. This method uses the intro sort
651    * algorithm, but falls back to insertion sort for small arrays.
652    * @param fromIndex start index (inclusive)
653    * @param toIndex end index (exclusive)
654    */
655   public static <T> void introSort(T[] a, int fromIndex, int toIndex, Comparator<? super T> comp) {
656     if (toIndex-fromIndex <= 1) return;
657     new ArrayIntroSorter<>(a, comp).sort(fromIndex, toIndex);
658   }
659   
660   /**
661    * Sorts the given array using the {@link Comparator}. This method uses the intro sort
662    * algorithm, but falls back to insertion sort for small arrays.
663    */
664   public static <T> void introSort(T[] a, Comparator<? super T> comp) {
665     introSort(a, 0, a.length, comp);
666   }
667   
668   /**
669    * Sorts the given array slice in natural order. This method uses the intro sort
670    * algorithm, but falls back to insertion sort for small arrays.
671    * @param fromIndex start index (inclusive)
672    * @param toIndex end index (exclusive)
673    */
674   public static <T extends Comparable<? super T>> void introSort(T[] a, int fromIndex, int toIndex) {
675     if (toIndex-fromIndex <= 1) return;
676     introSort(a, fromIndex, toIndex, ArrayUtil.<T>naturalComparator());
677   }
678   
679   /**
680    * Sorts the given array in natural order. This method uses the intro sort
681    * algorithm, but falls back to insertion sort for small arrays.
682    */
683   public static <T extends Comparable<? super T>> void introSort(T[] a) {
684     introSort(a, 0, a.length);
685   }
686 
687   // tim sorts:
688   
689   /**
690    * Sorts the given array slice using the {@link Comparator}. This method uses the Tim sort
691    * algorithm, but falls back to binary sort for small arrays.
692    * @param fromIndex start index (inclusive)
693    * @param toIndex end index (exclusive)
694    */
695   public static <T> void timSort(T[] a, int fromIndex, int toIndex, Comparator<? super T> comp) {
696     if (toIndex-fromIndex <= 1) return;
697     new ArrayTimSorter<>(a, comp, a.length / 64).sort(fromIndex, toIndex);
698   }
699   
700   /**
701    * Sorts the given array using the {@link Comparator}. This method uses the Tim sort
702    * algorithm, but falls back to binary sort for small arrays.
703    */
704   public static <T> void timSort(T[] a, Comparator<? super T> comp) {
705     timSort(a, 0, a.length, comp);
706   }
707   
708   /**
709    * Sorts the given array slice in natural order. This method uses the Tim sort
710    * algorithm, but falls back to binary sort for small arrays.
711    * @param fromIndex start index (inclusive)
712    * @param toIndex end index (exclusive)
713    */
714   public static <T extends Comparable<? super T>> void timSort(T[] a, int fromIndex, int toIndex) {
715     if (toIndex-fromIndex <= 1) return;
716     timSort(a, fromIndex, toIndex, ArrayUtil.<T>naturalComparator());
717   }
718   
719   /**
720    * Sorts the given array in natural order. This method uses the Tim sort
721    * algorithm, but falls back to binary sort for small arrays.
722    */
723   public static <T extends Comparable<? super T>> void timSort(T[] a) {
724     timSort(a, 0, a.length);
725   }
726 
727 }